package jpa4azure.impl;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import javassist.util.proxy.MethodHandler;
import jpa4azure.type.Key;
import jpa4azure.type.TypeWrapper;
import jpa4azure.type.TypeWrapperFactory;
import jpa4azure.type.Utils;

import org.apache.commons.lang.StringUtils;

public class DefaultMethodHandler extends ObjectMeta implements MethodHandler  {
	
	private static Method dirty;
	private static Method hashCode;
	private static Method equals;
	private AzureEntityManager aem;
	
	static {
		try {
			Persistable.class.getMethod("activate");
			dirty = Persistable.class.getMethod("dirty");
			hashCode = Object.class.getMethod("hashCode");
			equals = Object.class.getMethod("equals", Object.class); 
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public DefaultMethodHandler(AzureEntityManager aem) {
		this.aem = aem;
	}

	public Object invoke(Object target, Method method, Method proxyMethod, Object[] arg3)
			throws Throwable {
		if (method.equals(hashCode)) {
			return identityHash(target);
		} else if (method.equals(equals) && arg3.length == 1) {
			return equalsOverride(target, arg3[0]);
		}
		
		if (method.equals(dirty)) {
			return getDirtyProperties(); 
		} else if ( method.getName().startsWith("set") && isActive() ) {			
			String property = method.getName().substring(3);
			property = StringUtils.uncapitalize(property);
			addDirtyProperty(property);
		} else if (method.getName().startsWith("get") && isActive()) {
			doGet(target, method);
		}
		return ( proxyMethod != null) ? proxyMethod.invoke(target, arg3) : null;
	}

	private Object equalsOverride(Object a, Object b) {
		
		if (a == b) return true;
		if (b==null) return false;

		TypeWrapper t = TypeWrapperFactory.$(a);
		Key k1 = t.id(a);
		Key k2 = t.id(b);
		return k1.equals(k2);
	}

	private Object identityHash(Object target) {
		TypeWrapper t = TypeWrapperFactory.$(target);
		Key k = t.id(target);
		return k.hashCode();
	}

	private void doGet(Object target, Method method)
			throws IllegalAccessException {
		String property = method.getName().substring(3);
		property = StringUtils.uncapitalize(property);
		TypeWrapper type = TypeWrapperFactory.$(target);
		Field f = type.getField(property);
		if (f == null)
			return;
		if (! Utils.isEntity(f.getType()) )
			return;
		//if (!f.isAccessible())
		//	f.setAccessible(true);
		Object value = f.get(target);
		if (value!=null && aem.contains(value))
			return;
		Key key = getKey(property);
		if (key!=null) {
			Object o = new LoadFieldOperation(method, aem).get(key);	
			f.set(target, o);
		}
	}
}
